package com.lijianlove.keeplearning.dr.utils;

import com.lijianlove.keeplearning.dr.support.log.EventPublisher;
import com.lijianlove.keeplearning.dr.support.log.event.ClearTimedKeyEvent;

import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 带过期时间的map
 * 因为是基于内存的，如果可能有大量key ，建议过期时间设置的尽可能短一些
 *
 * @author lijian
 * @date 2019/4/30 下午5:54
 */
public class TimedConcurrentHashMap<K> extends ConcurrentHashMap<K, Long> {

    private static volatile TimedConcurrentHashMap<String> store;
    /**
     * 默认过期时间
     */
    private Long DEFAULT_EXPIRE = 1000 * 1 * 60 * 60 * 24 * 12L;

    private static ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor((ThreadFactory) r -> new Thread(r, "CLEAR_TIMED_KEY"));

    private static Lock lock = new ReentrantLock();
    private static Condition notEmpty = lock.newCondition();

    private TimedConcurrentHashMap() {
    }

    public static TimedConcurrentHashMap getInstance() {
        if (store == null) {
            synchronized (TimedConcurrentHashMap.class) {
                if (store == null) {
                    store = new TimedConcurrentHashMap<>();
                    registTask();
                }
            }
        }
        return store;
    }

    @Override
    public Long put(K key, Long value) {
        lock.lock();
        Long res;
        try {
            if (value > 0) {
                res = super.put(key, System.currentTimeMillis() + value);
            } else {
                res = super.put(key, System.currentTimeMillis() + DEFAULT_EXPIRE);
            }
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
        return res;
    }

    @Override
    public Long get(Object key) {
        try {
            lock.lock();
            Long expire = super.get(key);
            if (expire != null && System.currentTimeMillis() > expire) {
                store.remove(key);
                return null;
            }
            return expire;
        } finally {
            lock.unlock();
        }
    }

    private static void registTask() {
        executorService.scheduleAtFixedRate(() -> {
            for (String key : store.keySet()) {
                store.get(key);
            }
            if (store.size() == 0) {
                lock.lock();
                try {
                    ClearTimedKeyEvent event = new ClearTimedKeyEvent();
                    event.setEventType(ClearTimedKeyEvent.EventType.REGISTER_WAITING);
                    event.setTime(System.currentTimeMillis());
                    event.setKeyWord("registTask");
                    event.setMsg("registTask waiting");
                    EventPublisher.publish(event);

                    notEmpty.await();

                    ClearTimedKeyEvent clearTimedKeyEvent = new ClearTimedKeyEvent();
                    clearTimedKeyEvent.setEventType(ClearTimedKeyEvent.EventType.REGISTER_RESTARTED);
                    clearTimedKeyEvent.setTime(System.currentTimeMillis());
                    clearTimedKeyEvent.setKeyWord("registTask");
                    clearTimedKeyEvent.setMsg("registTask restart");

                    EventPublisher.publish(clearTimedKeyEvent);
                } catch (InterruptedException e) {
                } finally {
                    lock.unlock();
                }
            }
        }, 0, 10, TimeUnit.MILLISECONDS);

        ClearTimedKeyEvent event = new ClearTimedKeyEvent();
        event.setEventType(ClearTimedKeyEvent.EventType.REGISTER_INITED);
        event.setTime(System.currentTimeMillis());
        event.setKeyWord("registTask");
        event.setMsg("registTask restart");
        EventPublisher.publish(event);
    }
}
